package lumis.content.control.tag;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Locale;

import org.w3c.dom.Node;

import lumis.content.core.ContentTag;
import lumis.content.source.field.type.TagDataType;
import lumis.doui.control.DataBoundControl;
import lumis.doui.control.IClientSideReadableControl;
import lumis.doui.processaction.IProcessActionHandler;
import lumis.doui.render.IRenderable;
import lumis.portal.PortalException;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.serviceinstance.ServiceInstanceConfig;
import lumis.portal.serviceinstance.ServiceInstanceDependencyConfig;
import lumis.util.IResource;
import lumis.util.XmlUtil;
import lumis.util.converter.AbstractConverter;
import lumis.util.converter.IConverter;
import lumis.util.converter.UnsupportedConversionTypeException;

/**
 * Defines an input tag control that allows the user to enter any tag value. This is a data bound
 * control.
 * <p>
 * The value node gets the control value as its inputed, while the tags node is filled in alphabetical ascending order.
 * Example use:
 * <xmp>
 * <control type="lum_inputTag" id="tagId" rows="5" style="width:90%"/>
 * </xmp>
 * 
 * xml data available for xsl rendering:
 * <xmp>
 * <control id="tags" rows="5" style="width:90%" type="lum_inputTag">
 * 	<data>
 * 		<value>tag3, tag1, tag2</value>
 * 		<tags>
 * 			<tag>
 * 				<name>tag1</name>
 * 			</tag>
 * 			<tag>
 * 				<name>tag2</name>
 * 			</tag>
 * 			<tag>
 * 				<name>tag3</name>
 * 			</tag>
 * 		</tags>
 * 	</data>
 * </control> 
 * </xmp>
 * 
 * @version $Revision: 13092 $ $Date: 2011-05-28 18:19:06 -0300 (Sat, 28 May 2011) $
 * @since 6.0.0
 */
public class InputTagControl extends DataBoundControl implements IClientSideReadableControl
{
	@Override
	public IResource getControlResource() throws PortalException 
	{
		if (controlResource == null)
		{
			controlResource = super.getControlResource();
			controlResource.addResourcePath("lumis/content/control/tag/strings/strings");
		}

		return controlResource;
	}
	
	@Override
	protected Class<?> getValueClass() throws PortalException 
	{
		return String.class;
	}
	
	@Override
	public void buildSubControls() throws PortalException 
	{
		super.buildSubControls();
		
		StringBuilder subControlsXmlString = new StringBuilder();
		subControlsXmlString.append(XmlUtil.XML_HEADER);

		subControlsXmlString.append("<controls>");
		// get the tag service instance id related to this service instance
		ServiceInstanceConfig tagServiceInstance = ManagerFactory.getServiceInstanceManager().getServiceInstanceByDependency(
				douiContext.getRequest().getSessionConfig(), 
				douiContext.getRequest().getServiceInstanceConfig().getId(),
				ServiceInstanceDependencyConfig.DEPENDENCY_TYPE_TAG_REPOSITORY, douiContext.getTransaction());
		
		if(tagServiceInstance != null)
		{
			subControlsXmlString.append("<control type=\"lum_regularExpressionValidator\" id=\"tagValidator_"+getId()+"\" controlToValidateId=\""+getId()+"\" " +
					"expression=\"^\\s*[^{,]{0,30}(\\s*,\\s*[^{,]{0,30})*\\s*$\"  errorMessage=\"STR_INVALID_TAG_VALUE\" value=\"STR_DEFAULT_VALIDATOR_VALUE\" />");
		}
		subControlsXmlString.append("</controls>");

		appendSubControls(subControlsXmlString.toString());
	}
	
	@Override
	public void setRenderData() throws PortalException
	{
		super.setRenderData();
		// get the tag service instance id related to this service instance
		ServiceInstanceConfig tagServiceInstance = ManagerFactory.getServiceInstanceManager().getServiceInstanceByDependency(
				douiContext.getRequest().getSessionConfig(), 
				douiContext.getRequest().getServiceInstanceConfig().getId(),
				ServiceInstanceDependencyConfig.DEPENDENCY_TYPE_TAG_REPOSITORY, douiContext.getTransaction());
		
		if(tagServiceInstance != null)
		{
			Object currentValue = getValue();
			if(currentValue != null)
			{
				// add a display value
				XmlUtil.addTextNode(controlDefinitionNode, "data", "value", (String)currentValue);

				// add a strutured xml data
				Node dataNode = XmlUtil.addNode(controlDefinitionNode, "data");
				TagDataType.Data dataTag = getConverter().convert(TagDataType.Data.class, currentValue);
				XmlUtil.addNodeFromXmlString(dataNode, ((IRenderable)dataTag).getRenderData(this));
			}
		}
		else
		{
			// show a message error if has no repository associated.
			Node dataNode = XmlUtil.addNode(controlDefinitionNode, "data");
			XmlUtil.addTextNode(dataNode, "error", localize("STR_NO_TAG_SERVICE_INSTANCE_SPECIFIED"));
		}
	}

	/**
	 * Tag data converter.
	 * The supported conversions are {@link TagDataType.Data} to {@link String} and vice-versa.
	 * If some conversion is not supported is requested a {@link UnsupportedConversionTypeException} is given.
	 * 
	 * @version $Revision: 13092 $ $Date: 2011-05-28 18:19:06 -0300 (Sat, 28 May 2011) $
	 * @since 6.0.0
	 */
	private static class TagDataConverter extends AbstractConverter
	{
		@SuppressWarnings("unchecked")
		@Override
		protected <T> T doConversion(Class<T> type, Object value, Locale locale, String pattern) throws UnsupportedConversionTypeException
		{
			if(type.isAssignableFrom(TagDataType.Data.class))
			{
				Collection<TagDataType.Data.Tag> tagParameter = new ArrayList<TagDataType.Data.Tag>();
				if(value instanceof String)
				{
					String strValue = (String)value;
					if(value != null && strValue.length() > 0)
					{
						// split parameter by comma
						String[] tags = strValue.split(ContentTag.TAG_STRING_SEPARATOR);
						for(String strTag:tags)
						{
							// remove undesirable white spaces 
							strTag = strTag.trim();
							//add the tag on collection if its length is bigger than zero. 
							if(strTag != null && strTag.length() > 0)
							{
								TagDataType.Data.Tag tag = new TagDataType.Data.Tag();
								tag.setName(strTag);
								tagParameter.add(tag);
							}
						}
					}
					
					return (T)new TagDataType.Data(tagParameter);
				}
				else
				{
					throw new UnsupportedConversionTypeException("value: "+value.getClass());
				}
			}
			else if(type.isAssignableFrom(String.class))
			{
				StringBuffer sbValue = new StringBuffer(200);
				if(value instanceof TagDataType.Data)
				{
					TagDataType.Data valueData = (TagDataType.Data)value;
					for (Iterator iterator = valueData.getTags().iterator(); iterator.hasNext();) 
					{
						TagDataType.Data.Tag tag = (TagDataType.Data.Tag) iterator.next();
						sbValue.append(tag.getName());
						if(iterator.hasNext())
							sbValue.append(ContentTag.TAG_STRING_SEPARATOR+" ");
					}
				}
				
				return (T)sbValue.toString();
			}
			else
			{
				throw new UnsupportedConversionTypeException("type: "+type);
			}
		}
	}
	
	@Override
	protected IConverter createConverter() throws PortalException 
	{
		return new TagDataConverter();
	}

	protected void setProcessActionHandlerParameter(IProcessActionHandler processActionHandler, String parameterName) throws PortalException
	{
		if (getValue() != null)
			setProcessActionHandlerParameter(processActionHandler, parameterName, getValue(TagDataType.Data.class));
	}
}
